#include <stdlib.h>
#include "st.h"
#include "hyperlink.h"
#include "functor.h"
#include "internal_hash.h"


#include "util.h"
#include "error.h"
#include "slim_header/string.h"
#include "slim_header/port.h"

/* 配列のサイズ >= テーブルのサイズ になるはず */
#define INIT_ARY_SIZE 8

/* 環境変数群 */
HyperLinkSystem hl;

/* prototype */
void hyperlink_print();

/* 初期化処理 */
void lmn_hyperlink_init()
{
  hl.hyperlink_id = 1;
  hl.ary_size = INIT_ARY_SIZE;
  hl.func_to_name = NULL;
  hl.name = NULL;
  hl.coname = NULL;
  hl.fc_link_hl = NULL;//seiji

}

/* 解放処理 */
void lmn_hyperlink_destroy()
{
  int i, j;

//  hyperlink_print();

  if (hl.name != NULL) {
    for (i = 0, j = 0; i < hl.ary_size; i++, j--) {
      if (LMN_NAME(i) != NULL) {                // hl.name[i]が初期化されていて
        if (LMN_NAME_CHILD_TBL(i) != NULL)      // かつ表childrenを持つならば解放
          st_free_table(LMN_NAME_CHILD_TBL(i));
        LMN_FREE(LMN_NAME(i));
      }
    }
    LMN_FREE(hl.name);
  }

  if (hl.coname != NULL) {
    HyperLinkConame *coname;
    Vector *vec = vec_make(st_num(hl.coname));
    st_get_entries(hl.coname, vec, TRUE);
    for (j = 0; j < vec_num(vec); j++) {
      coname = (HyperLinkConame *)vec_get(vec, j);
      if (coname->children != NULL)
        st_free_table(coname->children);
      LMN_FREE(coname);
    }
    vec_free(vec);
    st_free_table(hl.coname);
  }

  if (hl.func_to_name != NULL)
    st_free_table(hl.func_to_name);


}

/* hl.func_to_nameが初期化されている ≒ hyperlinkが使用されている */
BOOL lmn_hyperlink_initialized()
{
  if (hl.func_to_name) return TRUE;
  else return FALSE;
}


/* 新しいhyperlink_idの生成 */
static HyperLink hyperlink_new_id()
{
  return hl.hyperlink_id++;
}


HyperLinkName *name_make(HyperLink h, LmnFunctor f)
{
  HyperLinkName *name;

  name = LMN_MALLOC(HyperLinkName);
  name->rank     = 0;
  name->element  = 0;
  name->func     = f;
//  name->coname = f; // conameを持たない場合はfuncと等しい
  name->parent   = h; // 親を持たない場合は自身
  name->children = NULL;

  return name;
}

HyperLinkConame *coname_make(HyperLink h, LmnFunctor f)
{
  HyperLinkConame *coname;

  coname = LMN_MALLOC(HyperLinkConame);
  coname->rank     = 0;
  coname->element  = 0;
  coname->func     = f;
  coname->parent   = h;
  coname->children = NULL;

  return coname;
}

LmnFunctor hyperlink_new_func(HyperLink h)
{
  LmnString name;
  char *c;
  LmnFunctor f;

  if (!lmn_env.hyperlink)
    lmn_fatal("Can't use hyperlink without option --hl.");

  if (lmn_env.nd)
    lmn_fatal("Can't use hyperlink with option --nd.");

  name = lmn_string_make(EXCLAMATION_NAME);
  c = int_to_str((long)h);
  lmn_string_push_raw_s(name, c);
  f = lmn_functor_intern(ANONYMOUS,
                         lmn_intern(lmn_string_c_str(name)),
                         1);

  lmn_string_free(name);
  lmn_free(c);

  return f;
}

/* 新規hyperlinkファンクタの生成 */
/* create new hyperlink functor */
LmnFunctor lmn_hyperlink_new_func()
{
  unsigned int i;
  HyperLink new_id;
  LmnFunctor f;

  /* create new name_id */
  new_id = hyperlink_new_id();

  /* create new name */
  f = hyperlink_new_func(new_id);

  /** 新規hyperlinkファンクタの登録 ここから */
  if (!hl.name) { // hl.nameが初期化されていない場合のみ
    hl.name = LMN_NALLOC(void *, hl.ary_size);
    for (i = 0; i < hl.ary_size; i++)
      hl.name[i] = NULL;
  }

  while (new_id >= hl.ary_size) { // hl.nameのサイズが足りない場合に拡張
    int root;

    root = hl.ary_size;
    hl.ary_size *= 2;
    hl.name = LMN_REALLOC(void *, hl.name,   hl.ary_size);

    for (; root < hl.ary_size; root++)
      hl.name[root] = NULL;

  }

  hl.name[new_id] = name_make(new_id, f);

  /* functor <-> hyperlink idの関係を設定 */
  if (!hl.func_to_name) hl.func_to_name = st_init_numtable();
  st_add_direct(hl.func_to_name, (st_data_t)f , (st_data_t)new_id);

  /** 新規hyperlinkファンクタの登録 ここまで */

  return f;
}

/*
 * name型hyperlink name_idがconameを持つ場合はそのファンクタを返す
 * conameを持たない場合は新規生成し、そのファンクタを返す
 */
LmnFunctor lmn_hyperlink_set_coname(HyperLink name_id)
{
  LmnFunctor f;
  st_data_t entry;
  HyperLink root_id;
  HyperLinkConame *coname;

  root_id = lmn_hyperlink_get_root(name_id);
  coname = NULL;

  if (root_id < 0) // root_idが負ならばconameを有する
    return lmn_hyperlink_hl_to_fc(root_id);

  LMN_NAME_PARENT(root_id) = root_id * -1;

  f = hyperlink_new_func(LMN_NAME_PARENT(root_id));
  st_insert_safe(hl.func_to_name, (st_data_t)f, (st_data_t)LMN_NAME_PARENT(root_id));

  if (!hl.coname)
    hl.coname = st_init_ptrtable();

  if (!st_lookup(hl.coname, (st_data_t)LMN_NAME_PARENT(root_id), &entry)) {
    coname = coname_make(LMN_NAME_PARENT(root_id), f);
    st_add_direct(hl.coname, (st_data_t)LMN_NAME_PARENT(root_id), (st_data_t)coname);
  } else {
    coname = (HyperLinkConame *)entry;
  }

  coname->element += LMN_NAME_ELEM(root_id);

  return f;
}

HyperLinkConame *hyperlink_get_coname(HyperLink h)
{
  st_data_t t;
  st_lookup(hl.coname, (st_data_t)h, &t);

  return (HyperLinkConame *)t;
}

/* hyperlinkでないファンクタの場合は0を返す */
HyperLink lmn_hyperlink_fc_to_hl(LmnFunctor f)
{
  if (!hl.func_to_name) return 0; // func_to_nameが初期化されていない場合

  st_data_t id;
  if (st_lookup(hl.func_to_name, f, &id)) return (HyperLink)id;

  return 0;
}

LmnFunctor lmn_hyperlink_hl_to_fc(HyperLink h)
{
  if (h > 0) return LMN_NAME_FUNC(h);
  else if (h < 0) return LMN_CONAME_FUNC(h);
  else return 0; //もしもhyperlink idが0ならば0を返す
}

LmnFunctor lmn_hyperlink_fc_to_rep_fc(LmnFunctor f)
{
  if (!lmn_is_hyperlink(f)) return f;

  return lmn_hyperlink_hl_to_fc(lmn_hyperlink_get_representative(lmn_hyperlink_fc_to_hl(f)));
}

/* ファンクタ f がnameであるか判定 */
BOOL lmn_is_hyperlink_name(LmnFunctor f)
{
  if (lmn_hyperlink_fc_to_hl(f) <= 0) return FALSE;

  return TRUE;
}

/* ファンクタ f がconameであるか判定 */
BOOL lmn_is_hyperlink_coname(LmnFunctor f)
{
  if (lmn_hyperlink_fc_to_hl(f) >= 0) return FALSE;

  return TRUE;
}

/* ファンクタ f がnameまたはconameであるか判定 */
BOOL lmn_is_hyperlink(LmnFunctor f)
{
  return (lmn_is_hyperlink_name(f) || lmn_is_hyperlink_coname(f));
}

/* (nameであると判定された)ファンクタfがconameを持つか判定 */
BOOL lmn_has_hyperlink_coname(LmnFunctor f)
{
  HyperLink h = lmn_hyperlink_get_root(lmn_hyperlink_fc_to_hl(f));
//  return (LMN_NAME_FUNC(h) != LMN_NAME_CONAME(h));
  return (LMN_NAME_PARENT(h) < 0);
}

void hyperlink_relink_children(HyperLink parent_id, Vector *children)
{
  int i, j, n;
  st_data_t t;
  HyperLink id, old_parent_id;

  n = vec_num(children);
  for (i = 0; i < n; i++) {

    id = (HyperLink)vec_get(children, i);

    if (parent_id != (old_parent_id = LMN_NAME_PARENT(id))) {

      LMN_NAME_PARENT(id) = parent_id;

      /* 旧親が持つ子集合から自身を削除 */
      st_delete(LMN_NAME_CHILD_TBL(old_parent_id), (st_data_t)id, &t);

      /* 自身からrootまでの経路上に存在する先祖のrankを調整 */
      for (j = i + 1; j < n; j++) {
        LMN_NAME_RANK((HyperLink)vec_get(children, j)) -= (LMN_NAME_RANK(id) + 1);
        LMN_NAME_ELEM((HyperLink)vec_get(children, j)) -= (LMN_NAME_ELEM(id) + 1);
      }

      if(!st_num(LMN_NAME_CHILD_TBL(old_parent_id))) { //子がいなくなった場合はテーブルを解放
        st_free_table(LMN_NAME_CHILD_TBL(old_parent_id));
        LMN_NAME_CHILD_TBL(old_parent_id) = NULL;
      }

      /* 新親が持つ子集合に自身を追加 */
      st_insert(LMN_NAME_CHILD_TBL(parent_id), (st_data_t)id, (st_data_t)id);

    }

  }
}

int lmn_hyperlink_rank_num(HyperLink h)
{
  if (!h) return 0;

  HyperLink rep = lmn_hyperlink_get_representative(h);
  if (rep > 0) return LMN_NAME_RANK(rep);
  else return LMN_CONAME_RANK(rep);
}

int lmn_hyperlink_element_num(HyperLink h)
{
  if (!h) return 0;

  HyperLink rep = lmn_hyperlink_get_representative(h);
  if (rep > 0) return LMN_NAME_ELEM(rep);
  else return LMN_CONAME_ELEM(rep);

}

/* f はhlink型チェック済でなければならない */
//void lmn_hyperlink_element_add(HyperLink h, int add)
//{
//  printf("add %d %d\n", h, add);
//  if (h > 0) {
//    LMN_NAME_ELEM(h) += add;
//  } else {
//    LMN_CONAME_ELEM(h) += add;
//  }
//  if (h != (h = lmn_hyperlink_get_representative(h))) {
//    if (h > 0) {
//      printf("  add %d\n", h);
//      LMN_NAME_ELEM(h) += add;
//    } else {
//      LMN_CONAME_ELEM(h) += add;
//    }
//  }
//}
void lmn_hyperlink_element_add(HyperLink h, int add)
{
  h = lmn_hyperlink_get_representative(h);
  if (h > 0) {
    LMN_NAME_ELEM(h) += add;
  } else {
    LMN_CONAME_ELEM(h) += add;
  }
}

/* f はhlink型チェック済でなければならない */
//void lmn_hyperlink_element_sub(HyperLink h, int sub)
//{
//  printf("sub %d %d\n", h, sub);
//  if (h > 0) {
//    LMN_NAME_ELEM(h) -= sub;
//  } else {
//    LMN_CONAME_ELEM(h) -= sub;
//  }
//  if (h != (h = lmn_hyperlink_get_representative(h))) {
//    if (h > 0) {
//      printf("  sub %d\n", h);
//      LMN_NAME_ELEM(h) -= sub;
//    } else {
//      LMN_CONAME_ELEM(h) -= sub;
//    }
//  }
//}
void lmn_hyperlink_element_sub(HyperLink h, int sub)
{
  h = lmn_hyperlink_get_representative(h);
  if (h > 0) {
    LMN_NAME_ELEM(h) -= sub;
  } else {
    LMN_CONAME_ELEM(h) -= sub;
  }
}

void lmn_hyperlink_free_mem(LmnMembrane *mem)
{
  AtomListEntry *al_ent;
  unsigned int i = 0;

  while (LMN_NAME(i)) {

    al_ent = lmn_mem_get_atomlist(mem, LMN_NAME_FUNC(i));
    if (al_ent)
    // simple hush tableの要素数を数える
    i++;
  }

}

/*
 * 子name nの親nameを返す
 * 親がいなければ自身を返す
 * rootまでの経路上にあるnameは記憶し、
 * 全てrootへの直リンクを貼り直す (path compression)
 */
//HyperLink lmn_hyperlink_get_root2(HyperLink name_id)
//{
//  HyperLink root_id = LMN_NAME_PARENT(name_id),
//            dst     = name_id;
//
//  if (root_id < 0 && name_id > 0) return name_id; // 親が負（coname）なら、name_idはその集合のroot
//
//  Vector *children = vec_make(LMN_NAME_RANK(root_id) * 2 + 1); //サイズはなんとなく
//
//  if (name_id > 0) { // name_idがnameの場合
//    while (root_id != dst) {
//      if (root_id < 0) break;
//      vec_push(children, (LmnWord)dst); //rootに直接リンクしている子までを記憶
//      dst = root_id;
//      root_id = LMN_NAME_PARENT(root_id);
//    }
//  } else {
//    while (root_id < 0 || root_id != dst) {
//      vec_push(children, (LmnWord)dst); //rootに直接リンクしている子までを記憶
//      dst = root_id;
//      root_id = LMN_NAME_PARENT(root_id);
//    }
//  }
//
//  if (vec_num(children) > 0)
//    hyperlink_relink_children(root_id, children);
//
//  vec_free(children);
//
//  return root_id;
//}

HyperLink lmn_hyperlink_get_root(HyperLink h)
{
  /*
   * 最終的に、
   *  root   : hを含む木のルート、
   *  root_pa: hを含む木のルートの親
   * が格納される
   */
  HyperLink root_pa, root;
  Vector *children;

  root = h;
  if (h > 0) { // name木をrootまで辿る
    root_pa = LMN_NAME_PARENT(h);

    if (root_pa < 0 || root_pa == root) return root;

    children = vec_make(LMN_NAME_RANK(root_pa) * 2 + 1);

    while (root_pa != root) {
      if (root_pa < 0) break;
      vec_push(children, (LmnWord)root);
      root = root_pa;
      root_pa = LMN_NAME_PARENT(root_pa);
    }
  } else { // coname木をrootまで辿る
    root_pa = LMN_CONAME_PARENT(h);

    if (root_pa == root) return root;

    children = vec_make(LMN_CONAME_RANK(root_pa) * 2 + 1);
    while (root_pa != root) {
      vec_push(children, (LmnWord)root);
      root = root_pa;
      root_pa = LMN_CONAME_PARENT(root_pa);
    }
  }

  if (vec_num(children) > 0) {
    if (h > 0 && root_pa > 0){
      hyperlink_relink_children(root_pa, children);
    }else if (h > 0 && root_pa < 0){
//      hyperlink_relink_children(root_pa * -1, children);
    }
  }
  vec_free(children);

  return root;
}

/*
 * hが所属する集合の代表要素を返す
 * 代表要素は、conameを持つ集合ならconame木のroot、持たない集合ならname木のrootとなる
 */
HyperLink lmn_hyperlink_get_representative(HyperLink h)
{
  if (h == 0) return 0;
  else if (h > 0) { //name
    HyperLink hr = lmn_hyperlink_get_root(h);
    if (LMN_NAME_PARENT(hr) != hr) //conameを持つ場合は、coname木のrootを返す
      return lmn_hyperlink_get_root(LMN_NAME_PARENT(hr));
    else return hr;
  }
  else //coname
    return lmn_hyperlink_get_root(h);

}

/* name以下の子をVectorに格納する */
void hyperlink_get_name_child(Vector *vec, HyperLink name, HyperLink ex)
{
  int i;
  HyperLink h;
  Vector *temp = vec_make(LMN_NAME_RANK(name));

  if (LMN_NAME_CHILD_TBL(name))
    st_get_entries(LMN_NAME_CHILD_TBL(name), temp, TRUE);

  for (i = 0; i < vec_num(temp); i++) {
    h = (HyperLink)vec_get(temp, i);
    if (LMN_NAME_RANK(h) > 0 && LMN_NAME_CHILD_TBL(h)) { // hが子を持つならば
      hyperlink_get_name_child(vec, h, ex);
    }
    if (h != ex) vec_push(vec, h);
  }
  vec_free(temp);

}

/* hが属する集合に属するhyperlink idを全てVectorに格納して返す
 * fidnproccxtで使用する関係上、Vectorの先頭には探索開始のidであるhが挿入される
 */
void lmn_hyperlink_get_children(Vector *vec, HyperLink h)
{
  HyperLink rep;

  vec_push(vec, (LmnWord)h);
  rep = lmn_hyperlink_get_representative(h);

  if (rep > 0) {
    if (rep != h) vec_push(vec, (LmnWord)rep);
    hyperlink_get_name_child(vec, rep, h);
  }
  //TODOseiji conameの場合も
}

/*
 * rankのより小さな方を子とする (union by rank)
 * 等しい場合は、root_id2をroot_id1の子とする
 */
HyperLink lmn_hyperlink_unify_n_n(HyperLink root_id1, HyperLink root_id2)
{
  HyperLink h1, h2;

  if (LMN_NAME_RANK(root_id1) >= LMN_NAME_RANK(root_id2)) {
    h1 = root_id1;
    h2 = root_id2;
  } else {
    h1 = root_id2;
    h2 = root_id1;
  }
  LMN_NAME_PARENT(h2) = h1;
  LMN_NAME_RANK(h1) = LMN_NAME_RANK(h1) + LMN_NAME_RANK(h2) + 1;

  //LMN_NAME_ELEM(h1) = LMN_NAME_ELEM(h1) + LMN_NAME_ELEM(h2);
  lmn_hyperlink_element_add(h1, LMN_NAME_ELEM(h2));

  if (!LMN_NAME_CHILD_TBL(h1))
    LMN_NAME_CHILD_TBL(h1) = st_init_numtable();
  st_add_direct(LMN_NAME_CHILD_TBL(h1), (st_data_t)h2, (st_data_t)h2);

  return h1;
}

/*
 * nameをconameの子とする
 * coname-->nameの参照を、coname * -1 = name(root) によって表現しているため、
 * union by rankの最適化は適用していない
 */
HyperLink lmn_hyperlink_unify_n_c(HyperLink name_id, HyperLink coname_id)
{

  HyperLink root1, root2;

  root1 = lmn_hyperlink_get_root(coname_id) * -1, // coname木のrootを参照しているname
  root2 = lmn_hyperlink_get_root(name_id);

  /* rankに関係なく、name_root2をname_root1に併合する */
  LMN_NAME_PARENT(root2) = root1;
  LMN_NAME_RANK(root1) = LMN_NAME_RANK(root1) + LMN_NAME_RANK(root2) + 1;

  lmn_hyperlink_element_add(root1, LMN_NAME_ELEM(root2));

  if (!LMN_NAME_CHILD_TBL(root1))
    LMN_NAME_CHILD_TBL(root1) = st_init_numtable();
  st_add_direct(LMN_NAME_CHILD_TBL(root1), (st_data_t)root2, (st_data_t)root2);

  return root1;
}

/*
 * rankのより小さな方を子とする (union by rank)
 * 等しい場合は、root_id2をroot_id1の子とする
 */
HyperLink lmn_hyperlink_unify_c_c(HyperLink root_id1, HyperLink root_id2)
{
  HyperLink h1, h2;

  if (LMN_CONAME_RANK(root_id1) >= LMN_CONAME_RANK(root_id2)) {
    h1 = root_id1;
    h2 = root_id2;
  } else {
    h1 = root_id2;
    h2 = root_id1;
  }
  LMN_CONAME_PARENT(h2) = h1;
  LMN_CONAME_RANK(h1) = LMN_CONAME_RANK(h1) + LMN_CONAME_RANK(h2) + 1;

  lmn_hyperlink_element_add(h1, LMN_CONAME_ELEM(h2));

  if (!LMN_CONAME_CHILD_TBL(h1))
    LMN_CONAME_CHILD_TBL(h1) = st_init_numtable();
  st_add_direct(LMN_CONAME_CHILD_TBL(h1), (st_data_t)h2, (st_data_t)h2);

  return h1;
}

/*
 * unify処理:
 *   異なる集合に属するhyperlinkであれば併合する
 *   同じ集合に属するhyperlinkを与えられた場合は失敗、0を返す
 *
 *   hyperlink h1, h2がそれぞれname, conameのどちらなのか
 *   nameの場合はconameを持つかどうかによって
 *   併合処理が分岐する
 */
HyperLink lmn_hyperlink_unify(HyperLink h1, HyperLink h2)
{
  /*
   * hがnameの場合、
   *   root: name木のroot name
   *   root_pa: hがconameを持つなら
   *
   *
   *
   *
   */
  HyperLink root1, root2, root_pa1, root_pa2,
            root_pa_root1, root_pa_root2, result;

  root1 = lmn_hyperlink_get_root(h1);
  root2 = lmn_hyperlink_get_root(h2);
  root_pa_root1 = 0;
  root_pa_root2 = 0;

  if (h1 == 0 || h2 == 0) return 0;
  if (root1 == root2) return 0;

  if (h1 > 0) root_pa1 = LMN_NAME_PARENT(root1);
  else root_pa1 = LMN_CONAME_PARENT(root1);

  if (h2 > 0) root_pa2 = LMN_NAME_PARENT(root2);
  else root_pa2 = LMN_CONAME_PARENT(root2);

  root_pa_root1 = lmn_hyperlink_get_root(root_pa1);
  root_pa_root2 = lmn_hyperlink_get_root(root_pa2);

  if (root_pa_root1 == root_pa_root2) return 0;

  /*
   * name自体は異なる集合に属するが、conameが同じ集合に属している場合、
   * あるいは一方がname, 他方がconameの場合には
   * rootの親で同集合に属するかを検査する
   */
//  if ((root_pa1 < 0 && root_pa2) &&
//      (lmn_hyperlink_get_root(root_pa1) == lmn_hyperlink_get_root(root_pa2))) return 0;

  if (h1 > 0 && h2 > 0) { // h1, h2共にname
    if (root_pa1 > 0 && root_pa2 > 0)        // h1, h2共にconameを持たない
      result = lmn_hyperlink_unify_n_n(root1, root2);
    else if (root_pa1 < 0 && root_pa2 > 0)  // h1がconameを持ち、h2は持たない
      result = lmn_hyperlink_unify_n_c(root2, root_pa1);
    else if (root_pa1 > 0 && root_pa2 < 0)  // h2がconameを持ち、h1は持たない
      result = lmn_hyperlink_unify_n_c(root1, root_pa2);
    else                                    // h1, h2共にconameを持つ
      result = lmn_hyperlink_unify_c_c(root_pa1, root_pa2);

  } else if (h1 < 0 && h2 > 0) { // h1がconame, h2がname
    if (root_pa2 > 0)                       // h2がconameを持たない
      result = lmn_hyperlink_unify_n_c(root2, root1);
    else                                    // h2がconameを持つ
      result = lmn_hyperlink_unify_c_c(root1, root_pa2);

  } else if (h1 > 0 && h2 < 0) { // h1がname, h2がconame
    if (root_pa1 > 0)                       // h1がconameを持たない
      result = lmn_hyperlink_unify_n_c(root1, root2);
    else                                    // h1がconameを持つ
      result = lmn_hyperlink_unify_c_c(root_pa1, root2);

  } else { // h1, h2共にconame
    result = lmn_hyperlink_unify_c_c(root1, root2);

  }

  return result;
}
/* fanc1, 2がHyperlinkファンクタでなければFALSE */
BOOL lmn_hyperlink_cmp_func(LmnFunctor func1, LmnFunctor func2)
{
  //printf("%d, %d\n", lmn_hyperlink_fc_to_hl(func1), lmn_hyperlink_fc_to_hl(func2));
  if (lmn_is_hyperlink(func1) && lmn_is_hyperlink(func2)) {
    return
      (lmn_hyperlink_get_representative(lmn_hyperlink_fc_to_hl(func1)) ==
       lmn_hyperlink_get_representative(lmn_hyperlink_fc_to_hl(func2)));
  }
  return FALSE;
}

BOOL lmn_hyperlink_cmp_atom(LmnAtom atom1, LmnLinkAttr attr1, LmnAtom atom2, LmnLinkAttr attr2)
{
  if (attr1 != attr2) return FALSE;
  if (LMN_ATTR_IS_DATA(attr1)) return FALSE;

  return lmn_hyperlink_cmp_func(LMN_SATOM_GET_FUNCTOR(atom1),
                                 LMN_SATOM_GET_FUNCTOR(atom2));
}

/* どちらもデータアトムでないことが分かっている */
//void lmn_hyperlink_set_linked_fc(LmnAtom atom1, LmnAtom atom2)
//{
//  LmnFunctor f1, f2;
//
//  f1 = LMN_SATOM_GET_FUNCTOR(atom1);
//  f2 = LMN_SATOM_GET_FUNCTOR(atom2);
//
//  if (lmn_is_hyperlink_name(f1))
//    LMN_NAME(lmn_hyperlink_fc_to_hl(f1))->linked = f2;
//  else if (lmn_is_hyperlink_coname(f1))
//    LMN_CONAME(lmn_hyperlink_fc_to_hl(f1))->linked = f2;
//
//  if (lmn_is_hyperlink_name(f2))
//    LMN_NAME(lmn_hyperlink_fc_to_hl(f2))->linked = f1;
//  else if (lmn_is_hyperlink_coname(f2))
//    LMN_CONAME(lmn_hyperlink_fc_to_hl(f2))->linked = f1;
//}

/* for debug */
void hyperlink_print()
{
  printf("== HyperLink ===============================================\n");

  if (!lmn_hyperlink_initialized()) {
    printf("There are no hyperlinks.\n");
  } else {

    int i, j;
    Vector *vec;

    printf("         %6s %8s %6s %6s\n", "[func]", "[parent]", "[elem]", " [rank (direct child)]");

    /* name */
    if (hl.name) {
      printf(" Name ----------------------------------------------------\n");
      for (i = 1; i < hl.ary_size; i++) {
        if (i < hl.hyperlink_id && LMN_NAME(i) != NULL) {
          printf("         %5s %8s ", LMN_HLINK_STR(i), LMN_HLINK_STR(LMN_NAME_PARENT(i)));

          if (i == LMN_NAME_PARENT(i)) printf("%6d  %6d ", LMN_NAME_ELEM(i), LMN_NAME_RANK(i));
          else printf("%6s  %6d ", "---", LMN_NAME_RANK(i));

          if ((LMN_NAME_RANK(i) > 0) || LMN_NAME_CHILD_TBL(i)) {
            printf("(");

            vec = vec_make(LMN_NAME_RANK(i));
            st_get_entries(LMN_NAME_CHILD_TBL(i), vec, TRUE);

            for (j = 0; j < vec_num(vec); j++) {
              if (j != 0) printf(",");
              printf("%d", (HyperLink)vec_get(vec, j));
            }
            vec_free(vec);
            printf(")");
          }
          printf("\n");
        }
      }
    }

    /* coname */
    if (hl.coname) {
      printf(" Co-Name -------------------------------------------------\n");

      Vector *conames;
      HyperLinkConame *coname;
      HyperLink h;

      conames = vec_make(st_num(hl.coname));
      st_get_entries(hl.coname, conames, TRUE);

      for (i = 0; i < vec_num(conames); i++) {

        coname = (HyperLinkConame *)vec_get(conames, i);
        h = lmn_hyperlink_fc_to_hl(coname->func);

        printf("         %5s %8s ", LMN_HLINK_STR(h), LMN_HLINK_STR(LMN_CONAME_PARENT(h)));

        if (h == LMN_CONAME_PARENT(h)) printf("%6d  %6d ", LMN_CONAME_ELEM(h), LMN_CONAME_RANK(h));
        else printf("%6s  %6d ", "---", LMN_CONAME_RANK(h));

        if ((LMN_CONAME_RANK(h) > 0) || LMN_CONAME_CHILD_TBL(h)) {
          printf("(%d,", -h);

          vec = vec_make(LMN_CONAME_RANK(h));
          st_get_entries(LMN_CONAME_CHILD_TBL(h), vec, TRUE);

          for (j = 0; j < vec_num(vec); j++) {
            if (j != 0) printf(",");
            printf("%d", (HyperLink)vec_get(vec, j));

          }
          vec_free(vec);
          printf(")");
        }
        printf("\n");
      }

      vec_free(conames);
    }


//    /* name */
//    if (hl.name) {
//      printf("/* -- names -- */\n");
//
//      for (i = 1; i < hl.ary_size; i++) {
//
//        if (i < hl.hyperlink_id && LMN_NAME(i) != NULL) {
//          printf("%4d: func %4s %4d, parent %3d, element ",
//                      i, lmn_id_to_name(LMN_FUNCTOR_NAME_ID(LMN_NAME_FUNC(i))),
//                      LMN_NAME_FUNC(i), LMN_NAME_PARENT(i));
//          if (i == LMN_NAME_PARENT(i)) printf("%3d, ", LMN_NAME_ELEM(i));
//          else printf("---, ");
//          //printf("%3d, ", LMN_NAME_ELEM(i));
//
//          printf("rank %3d, coname ", LMN_NAME_RANK(i));
//
//          if (LMN_NAME_PARENT(i) < 0)
//            printf("%3s\n", lmn_id_to_name(LMN_FUNCTOR_NAME_ID(lmn_hyperlink_hl_to_fc(LMN_NAME_PARENT(i)))));
//          else printf("---\n");
//
//          if (LMN_NAME_RANK(i) > 0 ) {
//            printf("      children >> ");
//            Vector *vec = vec_make(2);
//            if(LMN_NAME_CHILD_TBL(i) != NULL){
//              st_get_entries(LMN_NAME_CHILD_TBL(i), vec, TRUE);
//
//              for (j = 0; j < st_num(LMN_NAME_CHILD_TBL(i)); j++)
//                printf("%3d ", (HyperLink)vec_get(vec, j));
//                printf("\n");
//              }
//              vec_free(vec);
//          }
//        } else {
//  //        printf("%3d: ---\n", i);
//        }
//      }
//    }
//
//    /* coname */
//    if (hl.coname) {
//      printf("/* -- conames -- */\n");
//
//      Vector *conames = vec_make(8);
//      HyperLinkConame *coname;
//      HyperLink h;
//      st_get_entries(hl.coname, conames, TRUE);
//
//      for (i = 0; i < vec_num(conames); i++) {
//
//        coname = (HyperLinkConame *)vec_get(conames, i);
//        h = lmn_hyperlink_fc_to_hl(coname->func);
//
//        printf("%4d: func %4s %4d, parent %3d, element ",
//                    h, lmn_id_to_name(LMN_FUNCTOR_NAME_ID(coname->func)),
//                    coname->func, coname->parent);
//
//        if (lmn_hyperlink_fc_to_hl(coname->func) == coname->parent) printf("%3d, ", coname->element);
//        else printf("---, ");
//
//        printf("rank %3d\n", coname->rank);
//
//        if (coname->rank > 0 ) {
//          printf("      children >> ");
//          Vector *vec = vec_make(2);
//          if(coname->children != NULL){
//          st_get_entries(coname->children, vec, TRUE);
//
//          for (j = 0; j < st_num(coname->children); j++)
//            printf("%3d ", (HyperLink)vec_get(vec, j));
//          printf("\n");
//          }
//          vec_free(vec);
//        }
//      }
//      vec_free(conames);
//      printf("\n");
//    }
  }
//  Vector *vec = vec_make(10);
//  lmn_hyperlink_get_children(vec, 10);
//  int i;
//  printf(">%d\n", vec_num(vec));
//  for (i = 0; i < vec_num(vec); i++) {
//    printf("%d ", (HyperLink)vec_get(vec, i));
//  }
//  printf("\n");
  printf("============================================================\n");
}

void lmn_hyperlink_print()
{
  hyperlink_print();
}



/* ----------------------------------------- */

//st_table *same_proccxt_map = NULL; /* クローン→オリジナルの関係 */
st_table *proccxt_map = NULL;      /* アトム番号→自身がオリジナルかどうか */

unsigned int proccxt_id = 0;

void lmn_proccxt_init_map()
{
//  same_proccxt_map = st_init_ptrtable();
  proccxt_map      = st_init_ptrtable();
}

BOOL lmn_proccxt_has_map()
{
  return (proccxt_map != NULL);
}

BOOL lmn_proccxt_initialized(LmnInstrVar atom)
{
  st_data_t t;

//  printf("--hl %d, proccxt_map!=NULL %d", lmn_env.hyperlink, lmn_proccxt_has_map());
//  if (lmn_proccxt_has_map()) printf(", st_lookup %d\n", st_lookup(proccxt_map, (st_data_t)atom, &t)); else printf("\n");
  return
    (lmn_env.hyperlink &&
     lmn_proccxt_has_map() &&
     st_lookup(proccxt_map, (st_data_t)atom, &t));
}

/* proccxt_map, same_proccxt_map共にNULLではない */
void lmn_proccxt_clear()
{
  lmn_proccxt_destroy();
  proccxt_map = NULL;
//  same_proccxt_map = NULL;

}

void lmn_proccxt_destroy()
{
  Vector *apcs;
  AtomProcCxt *apc;
  unsigned int i, j;

  if (proccxt_map) {
    apcs = vec_make(st_num(proccxt_map));
    st_get_entries(proccxt_map, apcs, TRUE);
    for (i = 0; i < vec_num(apcs); i++) {
      apc = (AtomProcCxt *)vec_get(apcs, i);
      for (j = 0; j < LMN_APC_LEN(apc); j++) {
        if (LMN_APC_SPC(apc, j))
          LMN_FREE(LMN_APC_SPC(apc, j));
      }
      LMN_FREE(apc->spcs);

      if (apc->hl_tree) vec_free(apc->hl_tree);
//      LMN_FREE(apc->hl_tree);
      LMN_FREE(apc);
    }
    vec_free(apcs);

    st_free_table(proccxt_map);
  }

  /* same_proccxt_mapはapc, spcのポインタを格納しているだけなので
   * proccxt_mapの解放処理だけちゃんとやれば大丈夫
   */
//  if (same_proccxt_map != NULL) {
//    st_free_table(same_proccxt_map);
//  }

}

AtomProcCxt *lmn_proccxt_make_apc(int length)
{
  unsigned int i;
  AtomProcCxt *apc;

  apc = LMN_MALLOC(AtomProcCxt);
  apc->spcs = LMN_NALLOC(void *, length);
  for (i = 0; i < length; i++)
    apc->spcs[i] = NULL;
//    apc->spcs[i] = lmn_proccxt_make_dummy();
  apc->length = length;

//  apc->atom_ary = NULL;
  apc->hl_tree  = NULL;
  apc->start_attr = 0;

  return apc;
}

//void lmn_apc_init_atom_ary(AtomProcCxt *apc, int length)
//{
//  if (apc->atom_ary) LMN_FREE(apc->atom_ary);
//
//  apc->atom_ary = LMN_NALLOC(void *, length);
//
//}


//SameProcCxt **lmn_proccxt_apc_get_spcs(AtomProcCxt *apc)
//{
//  return apc->spcs;
//}

//void lmn_proccxt_destroy2()
//{
//  Vector *spcs_vec;
//  void **spcs;
//
//  if (proccxt_map) {
//    spcs_vec = vec_make(st_num(proccxt_map));
//    st_get_entries(proccxt_map, spcs_vec, TRUE);
//    for (i = 0; i < vec_num(spcs_vec); i++) {
//      spcs = (void **)vec_get(spcs_vec, i);
////      printf
//    }
//  }
//}


//SameProcCxt *lmn_proccxt_make(int atom, int arg, BOOL original)
SameProcCxt *lmn_proccxt_make(int atom, int arg, SameProcCxt *original)
{
  SameProcCxt *spc = LMN_MALLOC(SameProcCxt);
  spc->atom     = atom;
  spc->arg      = arg;
  spc->start    = 0;
//  spc->original = original;
  spc->original = original;
  return spc;
}

//SameProcCxt *lmn_proccxt_make_dummy()
//{
////  return lmn_proccxt_make(-1, -1, FALSE);
//  return lmn_proccxt_make(-1, -1, NULL);
//}

//void lmn_proccxt_set(SameProcCxt *spc, int atom, int arg, BOOL original)
void lmn_proccxt_set(SameProcCxt *spc, int atom, int arg, SameProcCxt *original)
{
  spc->atom     = atom;
  spc->arg      = arg;
  spc->start    = 0;
//  spc->original = original;
  spc->original = original;
}

void lmn_proccxt_init_dummy(SameProcCxt *spc)
{
  spc = lmn_proccxt_make(-1, -1, FALSE);
}

BOOL lmn_proccxt_is_dummy(SameProcCxt *spc)
{
  return (spc->atom < 0 && spc->arg < 0);
}





int lmn_proccxt_get_atom(SameProcCxt *spc)
{
  return spc->atom;
}

int lmn_proccxt_get_arg(SameProcCxt *spc)
{
  return spc->arg;
}


